﻿/* ######################################## *\
 * ### Copyright (C) 2009 AJ Ravindiran ### *
\* ######################################## */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

using AJRavindiran.Jolt.RuneScape.Models.Characters;
using AJRavindiran.Jolt.RuneScape.Models.Items;
using AJRavindiran.Jolt.RuneScape.Models.Items.Container;

namespace AJRavindiran.Jolt.RuneScape.Network.Packets
{
    /// <summary>
    /// Represents an opcode for player frames.
    /// </summary>
    public class CharacterFramesOpcode
    {
        #region Methods
        /// <summary>
        /// Set item display on an interface.
        /// </summary>
        /// <param name="character">Character to display items on interface to.</param>
        /// <param name="interfaceId">The interface to display the items on.</param>
        /// <param name="childId">The child interface on the main interface.</param>
        /// <param name="type">Type of interface.</param>
        /// <param name="container">The container holding the items.</param>
        public void SendItems(Character character, int interfaceId, int childId, int type, ItemContainer container)
        {
            character.GetStreamWriter()
                .CreateFrameVarSizeShort(255)
                .WriteShort(interfaceId)
                .WriteShort(childId)
                .WriteShort(type)
                .WriteShort(container.Length);
            for (int i = 0; i < container.Length; i++)
            {
                Item item = container.Get(i);
                int id, amount;
                if (item == null)
                {
                    id = -1;
                    amount = 0;
                }
                else
                {
                    id = item.Id;
                    amount = item.Amount;
                }
                if (amount > 254)
                    character.GetStreamWriter().WriteByteS(255).WriteIntV2(amount);
                else
                    character.GetStreamWriter().WriteByteS(amount);
                character.GetStreamWriter().WriteShortBigEndian(id + 1);
            }
            character.GetStreamWriter().EndFrameVarSizeShort();
        }

        /// <summary>
        /// Sends the needed login interfaces to the character.
        /// </summary>
        /// <param name="character">Character to send the interfaces to.</param>
        public void SendLoginInterfaces(Character character)
        {
            // Chatbox interfaces.
            SendTab(character, 11, 751); // Chat options
            SendTab(character, 68, 752); // Chatbox
            SendTab(character, 8, 137);  // Playername on chat
            SendTab(character, 7, 754);

            // Tab interfaces
            SendTab(character, 73, 92); // Attack tab
            SendTab(character, 74, 320); // Skill tab
            SendTab(character, 75, 274); // Quest tab
            SendTab(character, 76, 149); // Inventory tab
            SendTab(character, 77, 387); // Equicharacterment tab
            SendTab(character, 78, 271); // characterrayer tab
            SendTab(character, 79, 192); // Magic tab
            SendTab(character, 81, 550); // Friend tab
            SendTab(character, 82, 551); // Ignore tab
            SendTab(character, 83, 589); // Clan tab
            SendTab(character, 84, 261); // Setting tab
            SendTab(character, 85, 464); // Emote tab
            SendTab(character, 86, 187); // Music tab
            SendTab(character, 87, 182); // Logout tab

            // Orbs
            SendTab(character, 64, 748); // HP orb
            SendTab(character, 65, 749); // Prayer orb
            SendTab(character, 66, 750); // Energy orb
            SendTab(character, 67, 747); // Summoning orb
        }

        /// <summary>
        /// Setting a tab.
        /// </summary>
        /// <param name="character">Character to set tabs.</param>
        /// <param name="tabId">Which tab to display the interface on.</param>
        /// <param name="childId">The interface to display on the tab.</param>
        public void SendTab(Character character, int tabId, int childId)
        {
            SendInterface(character, 1, childId == 137 ? 752 : 548, tabId, childId);
        }

        /// <summary>
        /// Display an interface.
        /// </summary>
        /// <param name="character">Character to show interface.</param>
        /// <param name="showId">Sets the interface such as an overlay, etc.</param>
        /// <param name="windowId">What type of window you used, default should be 548.</param>
        /// <param name="interfaceId">Where to display it on the screen.</param>
        /// <param name="childId">The interface id to display.</param>
        public void SendInterface(Character character, int showId, int windowId, int interfaceId, int childId)
        {
            character.GetStreamWriter()
                .CreateFrame(93)
                .WriteShort(childId)
                .WriteByteA(showId)
                .WriteShort(windowId)
                .WriteShort(interfaceId);
        }

        /// <summary>
        /// Set either fullscreen or normal.
        /// </summary>
        /// <param name="character">Character to send window pane settings to.</param>
        /// <param name="pane">The frame set, 548 for the default setup.</param>
        public void SendWindowPane(Character character, int pane)
        {
            character.GetStreamWriter()
                .CreateFrame(239)
                .WriteShort(pane)
                .WriteByteA(0);
        }

        /// <summary>
        /// Set client configurations, such as running, prayer, hp, etc.
        /// </summary>
        /// <param name="character">The character to send the configs to.</param>
        /// <param name="id">The config id to set.</param>
        /// <param name="value">Value of the config. </param>
        public void SendPrimaryConfig(Character character, int id, int value)
        {
            if (value < 128)
                SendSecondaryConfig(character, id, value);
            else
                SendTertiaryConfig(character, id, value);
        }

        /// <summary>
        /// Secondary set client configurations.
        /// </summary>
        /// <param name="character">The character to send the configs to.</param>
        /// <param name="id">The config id to set.</param>
        /// <param name="value">Value of the config. </param>
        public void SendSecondaryConfig(Character character, int id, int value)
        {
            character.GetStreamWriter()
                .CreateFrame(100)
                .WriteShortA(id)
                .WriteByteA(value);
        }

        /// <summary>
        /// Tertiary set client configurations.
        /// </summary>
        /// <param name="character">The character to send the configs to.</param>
        /// <param name="id">The config id to set.</param>
        /// <param name="value">Value of the config. </param>
        public void SendTertiaryConfig(Character character, int id, int value)
        {
            character.GetStreamWriter()
                .CreateFrame(161)
                .WriteShort(id)
                .WriteIntV1(value);
        }

        /// <summary>
        /// Display a message in the chatbox.
        /// </summary>
        /// <param name="character">The character to display the char for.</param>
        /// <param name="message">The message to be shown in the chat box.</param>
        public void SendMessage(Character character, string message)
        {
            character.GetStreamWriter()
                .CreateFrameVarSize(218)
                .WriteString(message)
                .EndFrameVarSize();
        }

        /// <summary>
        /// Send the map region and other position information for the client.
        /// </summary>
        /// <param name="character">The character this frame should be created for.</param>
        public void SendMapRegion(Character character)
        {
            if (!ValidateMapRegion(character))
            {
                this.SendMessage(character, "This area of the world is unavailable, teleporting to spawn point...");
                character.SetLocation(RuneScape.GetConfigManager().General.SpawnPoint);
            }
            character.GetStreamWriter().CreateFrameVarSizeShort(142);
            bool forceSend = true;
            if ((((character.GetLocation().GetRegionX() / 8) == 48) || ((character.GetLocation().GetRegionX() / 8) == 49)) && ((character.GetLocation().GetRegionY() / 8) == 48))
                forceSend = false;
            if (((character.GetLocation().GetRegionX() / 8) == 48) && ((character.GetLocation().GetRegionY() / 8) == 148))
                forceSend = false;
            character.GetStreamWriter()
                .WriteShortA(character.GetLocation().GetRegionX())
                .WriteShortBigEndianA(character.GetLocation().GetLocalY())
                .WriteShortA(character.GetLocation().GetLocalX());

            for (int xCalc = (character.GetLocation().GetRegionX() - 6) / 8; xCalc <= ((character.GetLocation().GetRegionX() + 6) / 8); xCalc++)
            {
                for (int yCalc = (character.GetLocation().GetRegionY() - 6) / 8; yCalc <= ((character.GetLocation().GetRegionY() + 6) / 8); yCalc++)
                {
                    int region = yCalc + (xCalc << 1786653352);
                    if (forceSend || ((yCalc != 49) && (yCalc != 149) && (yCalc != 147) && (xCalc != 50) && ((xCalc != 49) || (yCalc != 47))))
                    {
                        int[] mapData = RuneScape.GetWorldManager().GetMapRegion(region);
                        if (mapData == null)
                        {
                            mapData = new int[4];
                            for (int i = 0; i < 4; i++)
                            {
                                mapData[i] = 0;
                            }
                        }
                        character.GetStreamWriter()
                            .WriteInt(mapData[0])
                            .WriteInt(mapData[1])
                            .WriteInt(mapData[2])
                            .WriteInt(mapData[3]);
                    }
                }
            }
            character.GetStreamWriter()
                .WriteByteC(character.GetLocation().Z)
                .WriteShort(character.GetLocation().GetRegionY())
                .EndFrameVarSizeShort();
            character.GetUpdateFlags().LastRegion = character.GetLocation();
        }

        /// <summary>
        /// Validates a certain mapregion to make sure it exists, if not, return to spawn point.
        /// </summary>
        /// <param name="character">The character to validate for.</param>
        /// <returns>Returns true if the mapregion exists and is usable.</returns>
        private bool ValidateMapRegion(Character character)
        {
            bool hasMapData = true;
            bool forceSend = true;

            if ((((character.GetLocation().GetRegionX() / 8) == 48) || ((character.GetLocation().GetRegionX() / 8) == 49)) && ((character.GetLocation().GetRegionY() / 8) == 48))
                forceSend = false;
            if (((character.GetLocation().GetRegionX() / 8) == 48) && ((character.GetLocation().GetRegionY() / 8) == 148))
                forceSend = false;
            for (int xCalc = (character.GetLocation().GetRegionX() - 6) / 8; xCalc <= ((character.GetLocation().GetRegionX() + 6) / 8); xCalc++)
            {
                for (int yCalc = (character.GetLocation().GetRegionY() - 6) / 8; yCalc <= ((character.GetLocation().GetRegionY() + 6) / 8); yCalc++)
                {
                    int region = yCalc + (xCalc << 1786653352);
                    if (forceSend || ((yCalc != 49) && (yCalc != 149) && (yCalc != 147) && (xCalc != 50) && ((xCalc != 49) || (yCalc != 47))))
                    {
                        int[] mapData = RuneScape.GetWorldManager().GetMapRegion(region);
                        if (mapData == null)
                        {
                            hasMapData = false;
                        }
                    }
                }
            }
            return hasMapData;
        }
        #endregion Methods
    }
}
